package echo.netty.testByteBuf;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.CompositeByteBuf;

import static io.netty.buffer.ByteBufUtil.appendPrettyHexDump;
import static io.netty.util.internal.StringUtil.NEWLINE;

/**
 * @author： zhangnan
 * @date： 2025/5/7 19:54
 * @description： TODO
 * @modifiedBy：
 * @version: 1.0
 */
public class TestSlice {
    public static void main(String[] args) {
        /**
         * 内存释放（retain&release）
         *  Netty采用引用计数法来控制回收内存，每个ByteByf都实现了ReferenceCounted接口
         *  1. 内阁ByteBuf对象的初始计数为 1
         *  2. 调用 release 方法计数减 1，如果计数为0，ByteBuf内存被回收
         *  3. 调用 retain 方法计数加 1，标识调用者没用完之前，其它handler即使调用了 release 也不会造成回收
         *  4. 当计数为0时，底层内存会被回收，这时即便ByteBuf对象还在，其各个方法均无法正常使用
         *  5. 使用基本原则
         *  谁负责 release，handler的head和tail都会做release处理，但是如果中间handler改变了ByteBuf的类型，head和tail则不会处理，所以释放的 基本原则是，谁时最后使用者
         *
         * 零拷贝
         *  零拷贝方法：
         *         slice：零拷贝的体现之一，对原始ByteBuf进行切片成多个ByteBuf，切片后的ByteBuf并没有发生内存复制，还是使用阮氏ByteBuf的内存，切片后ByteBuf维护独立的 read、write 指针;
         *         duplicate：零拷贝的体现之一，就好比截取了原始ByteBuf所有内容，并且没有maxcapacity的限制，也是与原始ByteBuf使用同一块底层内存，只是读写指针是独立的;
         *         composite: 零拷贝的体现之一，把多个ByteBuf从逻辑上合并到一起
         *     深度拷贝方法：
         *         以cope开头的方法，会将底层内存数据进行深拷贝，因此无论读写，都与原始ByteBuf无关
         */
        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(10);
        buf.writeBytes(new byte[]{'a','b','c','d','e','g','h','i','j'});
        log(buf);
        // 参数1：从那个开始切；参数2：切多长
        ByteBuf f1 = buf.slice(0, 5); // **注意 切片后的ByteBuf是不能扩容的，如果扩容会报错
        f1.retain();
        ByteBuf f2 = buf.slice(5, 5);
        f2.retain();
        log(f1);
        log(f2);
        f1.setByte(0,'b'); // 修改索引是0的元素内容为b
        log(f1);
        buf.release();// 如果原始ByteBuf释放了，那么后续如果操作切片会报错
        // 比如
        log(f1); // 这是可以在切片后的ByteBuf调用retain方法
        // 切片用完后自己再调用release方法
        f1.release();
        f2.release();

        System.out.println("++++++++++++++++++++++++++++++++++++");
        // composite方法测试,composite方法同样存在原始buf释放问题，所以也需要自己来掉用retain和release
        ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer();
        buf1.writeBytes(new byte[]{1,2,3,4,5});
        buf1.retain();
        ByteBuf buf2 = ByteBufAllocator.DEFAULT.buffer();
        buf2.writeBytes(new byte[]{6,7,8,9,10});
        buf2.retain();
        CompositeByteBuf buffer = ByteBufAllocator.DEFAULT.compositeBuffer();
        // 如果不加true的话，addComponents或addComponent都不会调整写入指针的位置，会出现无法读出数据的问题
        buffer.addComponents(true,buf1,buf2); // true会自动增长写指针
        log(buffer);
        buf1.release();
        buf2.release();



    }

    private static void log(ByteBuf buffer) {
        int length = buffer.readableBytes();
        int rows = length / 16 + (length % 15 == 0 ? 0 : 1) + 4;
        StringBuilder buf = new StringBuilder(rows * 80 * 2)
                .append("read index:").append(buffer.readerIndex())
                .append(" write index:").append(buffer.writerIndex())
                .append(" capacity:").append(buffer.capacity())
                .append(NEWLINE);
        appendPrettyHexDump(buf, buffer);
        System.out.println(buf.toString());
    }

}
